--[[
    1找出所有变体声明，取出，并且从code中剔除
        先用gmatch来找出，再用gsub来剔除
]]
Shader = {
    cache = {}
}
local function trim(s)
    return (string.gsub(s, "^%s*(.-)%s*$", "%1")) 
  end
local function splitSpace(s)
    local tbl = {}
    for w in string.gmatch(s, "[^%s]+") do
        table.insert(tbl, w)
    end
    return tbl
end
local function split(s, sep)
    local tbl = {}
    for w in string.gmatch(s, sep) do
        table.insert(tbl, w)
    end
    return table
end
function Shader:createShader(key, code)
    Console.log("createShader", key)
    local variants = {}
    local tbl = {
        variants = variants,
        shaders = {}
    }

    -- 找出所有变体定义
    for s in string.gmatch(code, "#variant.-\n") do
        local words = splitSpace(s)
        local variant = {key = words[2]}
        local min = tonumber(words[3])
        local max = tonumber(words[4])
        if min and max then
            variant.total = max - min + 1
            variant.min = min
            variant.max = max
        else
            variant.total = 2
            variant.min = 0
            variant.max = 1
        end
        variant.defineStrings = {}
        for i = variant.min, variant.max do
            table.insert(variant.defineStrings, "const int "..variant.key.." = "..i..';\n')
        end
        table.insert(variants, variant)
    end
    -- 删除变体定义
    code = string.gsub(code, "#variant.-\n", "")

    -- 找出变体判定逻辑，抽离出代码段
    -- 切割代码放进一个table 需要代码判定的加上判定条件
    local codeClip = ""
    local conditionKey = nil
    local conditionValue = nil
    local codeClips = {}
    for s in string.gmatch(code, ".-\n") do
        if string.find(s, "#ifvar") then
            table.insert(codeClips, {code = codeClip, conditionKey = conditionKey, conditionValue = conditionValue})
            codeClip = ""

            local t = string.match(s, "#ifvar%s+([%a_]+[%w_]*%s+=%s+[%d]+)\n")
            if t then
                local words = split(t, "=")
                conditionValue = tonumber(trim(words[2]))
                conditionKey = trim(words[1])
            else
                t = string.match(s, "#ifvar%s+([%a_]+[%w_]*)")
                if t then
                    conditionValue = 1
                    conditionKey = t
                else
                    error("编译shader错误！:"..s)
                end
            end
        elseif string.find(s, "#endvar") then
            table.insert(codeClips, {code = codeClip, conditionKey = conditionKey, conditionValue = conditionValue})
            codeClip = ""
            conditionKey = nil
            conditionValue = nil
        else
            codeClip = codeClip..s
        end
    end
    if codeClip then
        table.insert(codeClips, {code = codeClip, conditionKey = conditionKey, conditionValue = conditionValue})
    end
    tbl.codeClips = codeClips
    self.cache[key] = tbl
end

function Shader:get(key, variantOpts)
    variantOpts = variantOpts or {}
    local tbl = self.cache[key]
    local idx = 0
    local lastTotal = 0
    local opts = {}
    local define = ""
    for i, v in ipairs(tbl.variants) do
        local value = variantOpts[v.key]
        local num
        if value and type(value) == "number" then
            num = value - v.min
        elseif value == true then
            num = 1
        else
            num = 0
        end
        opts[v.key] = num + 1
        define = define..v.defineStrings[num + 1]
        idx = idx * lastTotal + num
        lastTotal = v.total
    end
    idx = idx + 1
    if not tbl.shaders[idx] then
        tbl.shaders[idx] = self:compile(define, tbl, opts)
    end
    return tbl.shaders[idx]
end

function Shader:compile(define, tbl, opts)
    for _, clip in ipairs(tbl.codeClips) do
        if not clip.conditionKey then
            define = define..clip.code
        else
            local value = opts[clip.conditionKey]
            if value == true then
                value = 2
            elseif value == false then
                value = 1
            end
            if value == clip.conditionValue then
                define = define..clip.code
            end
        end
    end
    
    return love.graphics.newShader(define)
end

Shader:createShader("default", [[
    #variant useMask
    #ifdef PIXEL
    vec4 effect( vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords )
    {
        #ifvar useMask

        #endvar
        vec4 texcolor = Texel(tex, texture_coords);
        return texcolor * color;     
    }
    #endif
    #ifdef VERTEX
    vec4 position( mat4 transform_projection, vec4 vertex_position )
    {

        return transform_projection * vertex_position;
    }
    #endif
]])